package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class UdpEchoServer {
    private DatagramSocket socket = null;
    public  UdpEchoServer(int port) throws SocketException {//手动指定一个端口号
        socket = new DatagramSocket(port);
        //这个程序一旦启动就需要关联/绑定上一个操作系统中的端口号(整数,区分主机上网络通信的程序)
        //一个主机上的一个端口号只能被一个进程绑定(除非进程一释放这个端口号)
        //反过来,一个进程可以绑定多个端口,端口号和socket是一一对应的,如果一个进程中有多个socket对象,自然就可以绑定上多个端口了

    }

    //服务器的启动逻辑
    public void start() throws IOException {//网络编程,读写socket本质上也是IO
        System.out.println("服务器启动!");
        while (true) {
            //重启服务器,可以直接杀进程
            //每次循环就是出来一个请求-响应过程

            //1.读取请求并解析
            DatagramPacket requestPcaket = new DatagramPacket(new byte[4096],4096);//每次创建一个socket对象就会搞一个缓冲区出来,每次网卡接收到数据再层层分用装进缓冲区里面,再receive拿走一个数据
            socket.receive(requestPcaket);//输出型参数DatagramPaket内置一个字节数组(通过这个字节数组保存收到的消息正文(应用层数据包)也就是UDP数据报载荷部分
            //此处receive就能从网卡读取到一个UDP数据包,就被放到了request对象中,其中UDP数据报的载荷部分就会被放到requestPacket内置的字节数组当中了
            //另外报头部分,也会被requestPacket的其他属性保存
            //把读到的字节数组,转换成String方便后续逻辑出来
            String request = new String((requestPcaket.getData()),0,requestPcaket.getLength());//基于字节数组构造出String,获得有效长度,不一定是最大长度

            //2.根据请求计算响应(对应回显服务器来说,这一步啥也不用做)
            String response = process(request);//根据请求构造响应

            //3.把响应返回到客户端
            //构造一个DatagramPcaket作为响应对象
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(StandardCharsets.UTF_8),response.getBytes().length,requestPcaket.getSocketAddress());//里面是String里面包含的字节数组,response.length()得到的长度为字符,而现在的写法是字节
            //requestPacket表示是客户端来的数据包,是把请求中的源IP,源端口,作为响应的目的IP和目的端口,此时就可以做到把消息返回给客户端这样的效果了
            //getSocketAddress是得到INetAddress对象,这个对象就包含了IP和端口号,和服务器通信的对选(对应的客户端)的ip和端口
            socket.send(responsePacket);
            //可以看出UDP是无连接的通信,UDP socket自身不保存对端的ip和端口,而是在每个数据报中有一个
            //另外代码中也没有"建立连接""接受连接"操作
            //面向数据包,send和receive都是以DatagramPacket为单位
            //全双工,一个socket既可以发送又可以接收

            //打印日志
            System.out.printf("[%s:%d] req: %s,resp: %s\n",responsePacket.getAddress().toString(),
                    requestPcaket.getPort(),request,response);

        }
    }
    public String process(String request) {
        //由于此处是回西安服务器,所以至少单纯的return
        //如果是一些具有特定业务的服务器,process就可以写其他任何吧想要的逻辑了

        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        //TODO 1.首先启动服务器
        server.start();//通常的使用端口号范围:  1024 < x < 65535
    }
}
